package com.papers.juan.service.impl;

import com.google.common.collect.Lists;
import com.papers.common.exception.ServiceException;
import com.papers.common.utils.JsonUtils;
import com.papers.common.utils.StringUtils;
import com.papers.juan.domain.TJuanPaperBakeup;
import com.papers.juan.domain.TJuanQuestion;
import com.papers.juan.domain.bean.PaperQuestionInfoBean;
import com.papers.juan.domain.bean.QuestionTypeJsonBean;
import com.papers.juan.mapper.TJuanPaperBakeupMapper;
import com.papers.juan.service.ITJuanPaperBakeupService;
import com.papers.juan.service.PaperExportService;
import com.spire.doc.Document;
import com.spire.doc.FieldType;
import com.spire.doc.FileFormat;
import com.spire.doc.HeaderFooter;
import com.spire.doc.Section;
import com.spire.doc.collections.ParagraphCollection;
import com.spire.doc.documents.HorizontalAlignment;
import com.spire.doc.documents.PageOrientation;
import com.spire.doc.documents.PageSize;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.documents.ParagraphStyle;
import com.spire.doc.documents.TextSelection;
import com.spire.doc.fields.DocPicture;
import com.spire.doc.fields.TextRange;
import com.spire.doc.fields.omath.OfficeMath;
import com.spire.doc.formatting.ParagraphFormat;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Decoder;

import javax.servlet.http.HttpServletResponse;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 试卷导出实现类
 *
 * @author libo
 * @date 2022/11/22 14:53
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PaperExportServiceImpl implements PaperExportService {

    /**
     * 换行标识
     */
    private static final String ROW_BREAK = "【ROW_BREAK】";

    private final TJuanPaperBakeupMapper juanPaperBakeupMapper;

    private final ITJuanPaperBakeupService juanPaperBakeupService;

    /**
     * 导出试卷word
     *
     * @param id       试卷备份id
     * @param response response响应
     */
    @Override
    public void exportPaper(HttpServletResponse response, String id) {
        // 试卷备份信息
        TJuanPaperBakeup juanPaperBakeup = juanPaperBakeupMapper.selectById(id);
        // 创建一个文档
        Document document = new Document();
        // 文档的节
        Section section = document.addSection();
        // 分两栏
        section.addColumn(350, 40);
        section.addColumn(350, 40);
        // 设置页边距
        section.getPageSetup().getMargins().setTop(40f);
        section.getPageSetup().getMargins().setBottom(40f);
        // 设置页面大小为A3
        section.getPageSetup().setPageSize(PageSize.A3);
        // 设置页面方向为Landscape横向
        section.getPageSetup().setOrientation(PageOrientation.Landscape);
        // 设置页码
//        setPageNumber(section);
        // 图片信息map
        Map<String, Map<String, Object>> imgInfoMap = new HashMap<>();
        // latex公式map
        Map<String, String> latexMap = new HashMap<>();
        // 生成试卷头部内容
        generalPaperHeader(document, section, juanPaperBakeup);
        // 生成试卷主体内容
        generalPaperContent(document, section, juanPaperBakeup, imgInfoMap, latexMap);
        // 转换latex
        transformLatex(document, section, latexMap);
        // 转换img
        transformImg(document, section, imgInfoMap);
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition", "attachment;filename=" + "paperName.docx");
        try {
            // 保存文档
            document.saveToFile(response.getOutputStream(), FileFormat.Docx);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出试卷答案word
     *
     * @param id       试卷备份id
     * @param response response响应
     */
    @Override
    public void exportPaperAnswer(HttpServletResponse response, String id) {
        // 试卷备份信息
        TJuanPaperBakeup juanPaperBakeup = juanPaperBakeupMapper.selectById(id);
        // 创建一个文档
        Document document = new Document();
        // 文档的节
        Section section = document.addSection();
        // 设置页边距
        section.getPageSetup().getMargins().setTop(40f);
        section.getPageSetup().getMargins().setBottom(40f);
        // 设置页面大小为A4
        section.getPageSetup().setPageSize(com.spire.doc.documents.PageSize.A4);
        // 图片信息map
        Map<String, Map<String, Object>> imgInfoMap = new HashMap<>();
        // latex公式map
        Map<String, String> latexMap = new HashMap<>();
        // 生成试卷头部内容
        generalPaperAnswerHeader(document, section, juanPaperBakeup);
        // 生成试卷答案主体内容
        generalPaperAnswerContent(document, section, juanPaperBakeup, imgInfoMap, latexMap);
        // 转换latex
        transformLatex(document, section, latexMap);
        // 转换img
        transformImg(document, section, imgInfoMap);
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition", "attachment;filename=" + "paperAnswerName.docx");
        try {
            // 保存文档
            document.saveToFile(response.getOutputStream(), FileFormat.Docx);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置页码
     *
     * @param section 文档的节
     */
    private static void setPageNumber(Section section) {
        // 获取节中的页脚
        HeaderFooter footer = section.getHeadersFooters().getFooter();
        // 添加段落到页脚
        Paragraph footerParagraph = footer.addParagraph();
        // 添加文字、页码域和总页数域到段落
        footerParagraph.appendText("第");
        footerParagraph.appendField("page number", FieldType.Field_Page);
        footerParagraph.appendText("页 共");
        footerParagraph.appendField("number of pages", FieldType.Field_Num_Pages);
        footerParagraph.appendText("页");
        // 将段落居中
        footerParagraph.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
    }

    /**
     * 生成试卷头部内容
     *
     * @param document        文档
     * @param section         文档的节
     * @param juanPaperBakeup 试卷备份信息
     */
    private static void generalPaperHeader(Document document, Section section, TJuanPaperBakeup juanPaperBakeup) {
        // 生成试卷名称
        generalPaperName(document, section, juanPaperBakeup);
        // 生成试卷介绍
        generalIntro(document, section, juanPaperBakeup);
        // 生成试卷注意事项
        generalAttention(document, section, juanPaperBakeup);
    }

    /**
     * 生成试卷答案头部内容
     *
     * @param document        文档
     * @param section         文档的节
     * @param juanPaperBakeup 试卷备份信息
     */
    private static void generalPaperAnswerHeader(Document document, Section section, TJuanPaperBakeup juanPaperBakeup) {
        // 生成试卷答案名称
        generalPaperAnswerName(document, section, juanPaperBakeup);
    }

    /**
     * 生成试卷主体内容
     *
     * @param document   文档
     * @param section    文档的节
     * @param juanPaper  试卷备份信息
     * @param imgInfoMap 图片信息map
     * @param latexMap   latex公式map
     */
    private void generalPaperContent(Document document, Section section, TJuanPaperBakeup juanPaper,
                                     Map<String, Map<String, Object>> imgInfoMap,
                                     Map<String, String> latexMap) {
        // 试卷题目信息json
        String paperQuestionInfo = juanPaper.getPaperQuestionInfo();
        List<QuestionTypeJsonBean> questionTypeJsonBeans = JsonUtils.parseArray(paperQuestionInfo, QuestionTypeJsonBean.class);
        // 题目列表
        List<TJuanQuestion> juanQuestions = juanPaperBakeupService.queryQuestionsById(juanPaper.getId());
        // key：题目id，value：题目内容
        Map<String, String> map = juanQuestions.stream().collect(Collectors.toMap(TJuanQuestion::getQuestionId,
            TJuanQuestion::getQuestionContent));
        // 大题索引号
        int bigQuestionIndex = 1;
        // 小题索引号
        int smallQuestionIndex = 1;
        // 遍历各题型（大题）
        for (QuestionTypeJsonBean questionTypeJsonBean : questionTypeJsonBeans) {
            Paragraph subTitleParagraph = section.addParagraph();
            // 子标题
            String subTitle = getLowerCaseChnNumberByArabicNumber(bigQuestionIndex) + "、" + questionTypeJsonBean.getBigName() +
                questionTypeJsonBean.getBigDesc();
            subTitleParagraph.appendText(subTitle);
            // 是否设置段落前自动间距
            boolean isBeforeAutoSpacing = false;
            if (bigQuestionIndex != 1) {
                isBeforeAutoSpacing = true;
            }
            // 设置子标题段落样式
            setSubTitleParagraphStyle("questionType" + bigQuestionIndex, document, subTitleParagraph, isBeforeAutoSpacing);
            // 当前题型下的题目列表
            List<PaperQuestionInfoBean> questions = questionTypeJsonBean.getQuestions();
            for (PaperQuestionInfoBean question : questions) {
                // 生成题干内容
                String content = generalQuestionStemContent(smallQuestionIndex, map.get(question.getQuestionId()), imgInfoMap,
                    latexMap, question.getScore());
                String[] contents = content.split(ROW_BREAK);
                for (int i = 0; i < contents.length; i++) {
                    Paragraph breakParagraph = section.addParagraph();
                    String breakContent = contents[i];
                    breakParagraph.appendText(breakContent);
                    // 是否设置段落后自动间距
                    boolean isAfterAutoSpacing = true;
                    if (i + 1 != contents.length) {
                        isAfterAutoSpacing = false;
                    }
                    setParagraphStyle(document, breakParagraph, question.getQuestionId() + i, false, Color.BLACK,
                        "宋体", 10.5f, HorizontalAlignment.Left, false, isAfterAutoSpacing,
                        true);
                }
                smallQuestionIndex++;
            }
            bigQuestionIndex++;
        }
    }

    /**
     * 生成试卷答案主体内容
     *
     * @param document   文档
     * @param section    文档的节
     * @param juanPaper  试卷备份信息
     * @param imgInfoMap 图片信息map
     * @param latexMap   latex公式map
     */
    private void generalPaperAnswerContent(Document document, Section section, TJuanPaperBakeup juanPaper,
                                           Map<String, Map<String, Object>> imgInfoMap,
                                           Map<String, String> latexMap) {
        // 试卷题目信息json
        String paperQuestionInfo = juanPaper.getPaperQuestionInfo();
        List<QuestionTypeJsonBean> questionTypeJsonBeans = JsonUtils.parseArray(paperQuestionInfo, QuestionTypeJsonBean.class);
        // 题目列表
        List<TJuanQuestion> juanQuestions = juanPaperBakeupService.queryQuestionsById(juanPaper.getId());
        // key：题目id，value：题目答案
        Map<String, String> map = juanQuestions.stream().collect(Collectors.toMap(TJuanQuestion::getQuestionId, TJuanQuestion::getAnswer));
        // 大题索引号
        int bigQuestionIndex = 1;
        // 小题索引号
        int smallQuestionIndex = 1;
        // 遍历各题型（大题）
        for (QuestionTypeJsonBean questionTypeJsonBean : questionTypeJsonBeans) {
            Paragraph subTitleParagraph = section.addParagraph();
            // 子标题
            String subTitle = getLowerCaseChnNumberByArabicNumber(bigQuestionIndex) + "、" + questionTypeJsonBean.getBigName();
            subTitleParagraph.appendText(subTitle);
            // 是否设置段落前自动间距
            boolean isBeforeAutoSpacing = false;
            if (bigQuestionIndex != 1) {
                isBeforeAutoSpacing = true;
            }
            // 设置子标题段落样式
            setSubTitleParagraphStyle("questionType" + bigQuestionIndex, document, subTitleParagraph, isBeforeAutoSpacing);
            // 当前题型下的题目列表
            List<PaperQuestionInfoBean> questions = questionTypeJsonBean.getQuestions();
            for (PaperQuestionInfoBean question : questions) {
                // 生成题目答案内容
                String content = generalQuestionAnswerContent(smallQuestionIndex, map.get(question.getQuestionId()), imgInfoMap, latexMap);
                String[] contents = content.split(ROW_BREAK);
                for (int i = 0; i < contents.length; i++) {
                    Paragraph breakParagraph = section.addParagraph();
                    String breakContent = contents[i];
                    breakParagraph.appendText(breakContent);
                    // 是否设置段落后自动间距
                    boolean isAfterAutoSpacing = true;
                    if (i + 1 != contents.length) {
                        isAfterAutoSpacing = false;
                    }
                    setParagraphStyle(document, breakParagraph, question.getQuestionId() + i,
                        false, Color.BLACK, "宋体", 12f, HorizontalAlignment.Left,
                        false, isAfterAutoSpacing, true);
                }
                smallQuestionIndex++;
            }
            bigQuestionIndex++;
            //大题里面需要重新从1开始
            smallQuestionIndex = 1;
        }
    }

    /**
     * base64转inputStream
     *
     * @param base64string base64字符串
     * @return 输入流
     */
    private static InputStream base2InputStream(String base64string) {
        ByteArrayInputStream stream = null;
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] bytes1 = decoder.decodeBuffer(base64string);
            stream = new ByteArrayInputStream(bytes1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stream;
    }

    private static String latexFormat(String latex) {
        if (latex.contains("leqslant")) {
            latex = latex.replace("leqslant", "leq");
        }
        if (latex.contains("geqslant")) {
            latex = latex.replace("geqslant", "geq");
        }
        StringBuilder latexBuilder = new StringBuilder();
        boolean isChinese = false;
        String regexStr = "[\u4E00-\u9FA5]";
        for (Character c : latex.toCharArray()) {
            Matcher chineseMatch = Pattern.compile(regexStr).matcher(c.toString());
            if (chineseMatch.find()) {
                if (isChinese) {
                    latexBuilder.append(c);
                } else {
                    latexBuilder.append("\\mbox{").append(c);
                    isChinese = true;
                }
            } else {
                if (isChinese) {
                    isChinese = false;
                    latexBuilder.append("}");
                }
                latexBuilder.append(c);
            }
        }
        return latexBuilder.toString();
    }

    /**
     * 生成试卷名称
     *
     * @param document        文档
     * @param section         文档的节
     * @param juanPaperBakeup 试卷备份信息
     */
    private static void generalPaperName(Document document, Section section, TJuanPaperBakeup juanPaperBakeup) {
        Paragraph paperNameParagraph = section.addParagraph();
        paperNameParagraph.appendText(juanPaperBakeup.getPaperName());
        // 设置标题段落样式
        setTitleParagraphStyle("paperName", document, paperNameParagraph);
    }

    /**
     * 生成试卷答案名称
     *
     * @param document        文档
     * @param section         文档的节
     * @param juanPaperBakeup 试卷备份信息
     */
    private static void generalPaperAnswerName(Document document, Section section, TJuanPaperBakeup juanPaperBakeup) {
        Paragraph paperAnswerNameParagraph = section.addParagraph();
        paperAnswerNameParagraph.appendText(juanPaperBakeup.getPaperName() + "答案");
        // 设置标题段落样式
        setTitleParagraphStyle("paperAnswerName", document, paperAnswerNameParagraph);
    }

    /**
     * 生成试卷介绍
     *
     * @param document  doc
     * @param section   试卷信息
     * @param juanPaper 试卷信息
     */
    private static void generalIntro(Document document, Section section, TJuanPaperBakeup juanPaper) {
        Paragraph introParagraph = section.addParagraph();
        List<PaperQuestionInfoBean> questions = getQuestions(juanPaper.getPaperQuestionInfo());
        // 介绍
        String paperIntro = "本试卷共" + questions.size() + "题，考试时间" + juanPaper.getPaperTime() + "分钟，满分"
            + juanPaper.getPaperTotalScore() + "分";
        introParagraph.appendText(paperIntro);
        setParagraphStyle(document, introParagraph, "paperIntro", false, Color.BLACK, "宋体",
            12f, HorizontalAlignment.Center, false, true, false);
    }

    /**
     * 生成试卷注意事项
     *
     * @param document  doc
     * @param section   试卷信息
     * @param juanPaper 试卷信息
     */
    private static void generalAttention(Document document, Section section, TJuanPaperBakeup juanPaper) {
        // 注意事项
        String paperAttention = juanPaper.getPaperAttention();
        if (!StringUtils.isEmpty(paperAttention)) {
            Paragraph attentionParagraph = section.addParagraph();
            attentionParagraph.appendText("*注意事项：" + paperAttention);
            setParagraphStyle(document, attentionParagraph, "paperAttention", false, Color.BLACK, "宋体",
                12f, HorizontalAlignment.Left, false, true, false);
        }
    }

    /**
     * 生成题干内容
     *
     * @param index      题目序号
     * @param content    内容
     * @param imgInfoMap 图片信息map
     * @param latexMap   latex公式map
     * @param score      题目分数
     * @return 内容
     */
    private static String generalQuestionStemContent(int index, String content, Map<String, Map<String, Object>> imgInfoMap,
                                                     Map<String, String> latexMap,
                                                     Integer score) {
        return index + "(" + score + "分)" + "." + htmlToText(content, imgInfoMap, latexMap);
    }

    /**
     * 生成题目答案内容
     *
     * @param index      题目序号
     * @param content    内容
     * @param imgInfoMap 图片信息map
     * @param latexMap   latex公式map
     * @return 内容
     */
    private static String generalQuestionAnswerContent(int index, String content, Map<String, Map<String, Object>> imgInfoMap,
                                                       Map<String, String> latexMap) {
        return index + "." + htmlToText(content, imgInfoMap, latexMap);
    }

    /**
     * 设置标题段落样式
     *
     * @param styleName      样式名称
     * @param document       文档
     * @param titleParagraph 标题段落
     */
    private static void setTitleParagraphStyle(String styleName, Document document, Paragraph titleParagraph) {
        setParagraphStyle(document, titleParagraph, styleName, true, Color.BLACK, "宋体", 18f,
            HorizontalAlignment.Center, false, true, false);
    }

    /**
     * 创建子标题段落样式
     *
     * @param styleName           样式名称
     * @param document            文档
     * @param subTitleParagraph   子标题段落
     * @param isBeforeAutoSpacing 是否设置段落前自动间距
     */
    private static void setSubTitleParagraphStyle(String styleName, Document document, Paragraph subTitleParagraph,
                                                  boolean isBeforeAutoSpacing) {
        setParagraphStyle(document, subTitleParagraph, styleName, true, Color.BLACK, "宋体", 12f,
            HorizontalAlignment.Left, isBeforeAutoSpacing, true, false);
    }

    /**
     * 设置正文段落样式
     *
     * @param styleName 样式名称
     * @param document  文档
     * @param paragraph 段落
     */
    private static void setContentPStyle(String styleName, Document document, Paragraph paragraph) {
        setParagraphStyle(document, paragraph, styleName, false, Color.BLACK, "宋体", 21f,
            HorizontalAlignment.Left, false, true, false);
    }

    /**
     * 设置段落样式
     *
     * @param document            文档
     * @param paragraph           段落
     * @param styleName           样式名称
     * @param isBold              是否加粗
     * @param color               字体颜色
     * @param fontName            字体名称
     * @param fontSize            字体大小
     * @param horizontalAlignment 对齐枚举
     * @param isBeforeAutoSpacing 是否设置段落前自动间距
     * @param isAfterAutoSpacing  是否设置段落后自动间距
     * @param isLineSpacing       是否设置段落行距
     */
    private static void setParagraphStyle(Document document, Paragraph paragraph, String styleName, boolean isBold, Color color,
                                          String fontName, float fontSize, HorizontalAlignment horizontalAlignment,
                                          boolean isBeforeAutoSpacing, boolean isAfterAutoSpacing, boolean isLineSpacing) {
        ParagraphStyle paperNameStyle = new ParagraphStyle(document);
        // 段落样式名称，当前试卷唯一
        paperNameStyle.setName(styleName);
        // 字体加粗
        paperNameStyle.getCharacterFormat().setBold(isBold);
        // 字体颜色
        paperNameStyle.getCharacterFormat().setTextColor(color);
        // 字体名称
        paperNameStyle.getCharacterFormat().setFontName(fontName);
        // 字体大小
        paperNameStyle.getCharacterFormat().setFontSize(fontSize);
        document.getStyles().add(paperNameStyle);
        paragraph.applyStyle(styleName);
        ParagraphFormat format = paragraph.getFormat();
        // 对齐方式
        format.setHorizontalAlignment(horizontalAlignment);

        if (isBeforeAutoSpacing) {
            // 段落前间距
            format.setBeforeAutoSpacing(false);
            format.setBeforeSpacing(40);
        }

        if (isAfterAutoSpacing) {
            // 段落后间距
            format.setAfterAutoSpacing(false);
            format.setAfterSpacing(20);
        }

        if (isLineSpacing) {
            // 段落行距，默认1.5倍行距
            format.setLineSpacing(1.5f * fontSize);
        }

    }

    /**
     * 生成无用的最后一段，用于移除水印（free版本不生成水印）
     *
     * @param section 文档的节
     */
    private static void generalLastParagraph(Section section) {
        Paragraph lastParagraph = section.addParagraph();
        lastParagraph.appendText("");
    }

    /**
     * 转换latex
     *
     * @param document 文档
     * @param section  文档的节
     * @param latexMap latex公式map
     */
    private static void transformLatex(Document document, Section section, Map<String, String> latexMap) {
        // 所有段落
        ParagraphCollection paragraphs = section.getParagraphs();
        // 段落数量
        int paragraphCount = paragraphs.getCount();
        // 遍历所有段落
        for (int i = 0; i < paragraphCount; i++) {
            // 段落信息
            Paragraph paragraph = paragraphs.get(i);
            // 段落文本
            String paragraphText = paragraph.getText();
            for (String key : latexMap.keySet()) {
                if (paragraphText.contains(key)) {
                    // latex公式
                    String latex = latexMap.get(key);
                    // 查找文档中与key匹配的所有匹配项，实际只会存在一条
                    TextSelection[] selections = document.findAllString(key, true, true);
                    // 将指定的匹配项替换为word能解析的公式
                    // 创建 OfficeMath 类的对象并载入公式
                    OfficeMath officeMath = new OfficeMath(paragraph.getDocument());
                    paragraph.getItems().add(officeMath);
                    officeMath.fromLatexMathCode(latex);
                    TextSelection textSelection = selections[0];
                    TextRange textRange = textSelection.getAsOneRange();
                    int index = textRange.getOwnerParagraph().getChildObjects().indexOf(textRange);
                    textRange.getOwnerParagraph().getChildObjects().insert(index, officeMath);
                    textRange.getOwnerParagraph().getChildObjects().remove(textRange);
                }
            }
        }
    }

    /**
     * 转换img
     *
     * @param document   文档
     * @param section    文档的节
     * @param imgInfoMap 图片信息map
     */
    private static void transformImg(Document document, Section section, Map<String, Map<String, Object>> imgInfoMap) {
        // 所有段落
        ParagraphCollection paragraphs = section.getParagraphs();
        // 段落数量
        int paragraphCount = paragraphs.getCount();
        // 遍历所有段落
        for (int i = 0; i < paragraphCount; i++) {
            // 段落信息
            Paragraph paragraph = paragraphs.get(i);
            // 段落文本
            String paragraphText = paragraph.getText();
            for (String key : imgInfoMap.keySet()) {
                if (paragraphText.contains(key)) {
                    Map<String, Object> imgMap = imgInfoMap.get(key);
                    String imgSuffix = (String) imgMap.get("imgSuffix");
                    String imgSrc = (String) imgMap.get("src");
                    // base64字符串中需要被替换的字符串
                    String targetString = getTargetStringImgSuffix(imgSuffix);
                    InputStream inputStream = base2InputStream(imgSrc.replace(targetString, ""));
                    // 查找文档中与key匹配的所有匹配项，实际只会存在一条
                    TextSelection[] selections = document.findAllString(key, true, true);
                    // 将指定的匹配项替换为图片
                    // 创建 DocPicture 类的对象并载入图片
                    DocPicture pic = new DocPicture(document);
                    pic.loadImage(inputStream);
                    int width = Integer.parseInt(imgMap.get("width").toString());
                    int height = Integer.parseInt(imgMap.get("height").toString());
                    // 图片宽度
                    pic.setWidth(width);
                    // 图片高度
                    pic.setHeight(height);
                    TextSelection textSelection = selections[0];
                    TextRange textRange = textSelection.getAsOneRange();
                    int index = textRange.getOwnerParagraph().getChildObjects().indexOf(textRange);
                    textRange.getOwnerParagraph().getChildObjects().insert(index, pic);
                    textRange.getOwnerParagraph().getChildObjects().remove(textRange);
                }
            }
        }
    }

    /**
     * 根据图片后缀获取base64的替换字符串
     *
     * @param imgSuffix 图片后缀
     * @return base64中需要被替换的字符串
     */
    private static String getTargetStringImgSuffix(String imgSuffix) {
        if ("ico".equals(imgSuffix)) {
            return "data:image/x-icon;base64,";
        } else {
            return "data:image/" + imgSuffix + ";base64,";
        }
    }

    /**
     * 移除水印（free版本不生成水印）
     *
     * @param targetPath 需要移除水印的文件地址
     * @param filePath   生成新文件的地址
     */
    private static void removeWaterMark(String targetPath, String filePath) {
        try (FileInputStream in = new FileInputStream(targetPath)) {
            XWPFDocument xwpfDocument = new XWPFDocument(OPCPackage.open(in));
            List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
            if (paragraphs.size() < 1) {
                return;
            }
            XWPFParagraph firstParagraph = paragraphs.get(0);
            XWPFParagraph lastParagraph = paragraphs.get(paragraphs.size() - 1);
            if (firstParagraph.getText().contains("Evaluation Warning: The document was created with Spire.Doc for JAVA.")) {
                xwpfDocument.removeBodyElement(xwpfDocument.getPosOfParagraph(firstParagraph));
//                xwpfDocument.removeBodyElement(xwpfDocument.getPosOfParagraph(lastParagraph));
            }
            OutputStream out = new FileOutputStream(filePath);
            xwpfDocument.write(out);
            in.close();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            new File(targetPath).delete();
        }
    }


    /**
     * 获取题目列表
     *
     * @param paperQuestionInfo 试卷题目信息json
     * @return 题目列表
     */
    private static List<PaperQuestionInfoBean> getQuestions(String paperQuestionInfo) {
        List<PaperQuestionInfoBean> questions = Lists.newArrayList();
        List<QuestionTypeJsonBean> questionTypeJsonBeans = JsonUtils.parseArray(paperQuestionInfo, QuestionTypeJsonBean.class);
        for (QuestionTypeJsonBean questionTypeJsonBean : questionTypeJsonBeans) {
            questions.addAll(questionTypeJsonBean.getQuestions());
        }
        return questions;
    }

    /**
     * html内容转换为text，图片信息使用文字标识替换并存储到map
     *
     * @param html       html内容
     * @param imgInfoMap 图片信息map
     * @param latexMap   latex公式map
     * @return html的文本信息
     */
    private static String htmlToText(String html, Map<String, Map<String, Object>> imgInfoMap, Map<String, String> latexMap) {
        if (StringUtils.isEmpty(html)) {
            return "";
        }
        Element body = Jsoup.parse(html).body();
        Elements imgElements = body.getElementsByTag("img");
        for (Element element : imgElements) {
            String dataLatex = element.attr("data-latex");
            String src = element.attr("src");
            String key;
            if (!"".equals(dataLatex)) {
                key = "#{latex" + latexMap.size() + "}";
                latexMap.put(key, dataLatex);
            } else {
                String imgSuffix = getImgSuffixByBase64String(src);
                Map<String, Object> map = new HashMap<>();
                map.put("width", StringUtils.isEmpty(element.attr("width")) ? "200" : element.attr("width"));
                map.put("height", StringUtils.isEmpty(element.attr("height")) ? "60" : element.attr("height"));
                map.put("src", src);
                map.put("imgSuffix", imgSuffix);
                key = "#{imgPath" + imgInfoMap.size() + "}";
                imgInfoMap.put(key, map);
            }
            element.replaceWith(new TextNode(key));
        }

        // 带有p标签的做换行处理
        Elements pElements = body.getElementsByTag("p");
        if (pElements.size() > 1) {
            for (int i = 1; i < pElements.size(); i++) {
                Element element = pElements.get(i);
                element.before("<p><span>" + ROW_BREAK + "</span></p>");
            }
        }

        // 带有br标签的做换行处理
        Elements brElements = body.getElementsByTag("br");
        if (brElements.size() > 0) {
            for (Element element : brElements) {
                element.before("<p><span>" + ROW_BREAK + "</span></p>");
            }
        }

        return body.text();
    }

    /**
     * 根据base64字符串获取图片后缀
     *
     * @param base64String base64字符串
     * @return 图片后缀
     */
    private static String getImgSuffixByBase64String(String base64String) {
        String imgSuffix;
        if (base64String.contains("data:image/jpeg;")) {
            // 编码的jpeg图片数据
            imgSuffix = "jpeg";
        } else if (base64String.contains("data:image/x-icon;")) {
            // 编码的icon图片数据
            imgSuffix = "ico";
        } else if (base64String.contains("data:image/gif;")) {
            // 编码的gif图片数据
            imgSuffix = "gif";
        } else if (base64String.contains("data:image/png;")) {
            // 编码的png图片数据
            imgSuffix = "png";
        } else if (base64String.contains("data:image/jpg;")) {
            // 编码的png图片数据
            imgSuffix = "jpg";
        } else {
            throw new ServiceException("上传图片格式不合法");
        }
        return imgSuffix;
    }

    /**
     * 根据阿拉伯数字获取小写中文数字
     *
     * @param number 阿拉伯数字
     * @return 小写中文数字
     */
    private static StringBuilder getLowerCaseChnNumberByArabicNumber(int number) {
        // 数字对应的汉字
        String[] num = {"一", "二", "三", "四", "五", "六", "七", "八", "九"};
        // 单位
        String[] unit = {"", "十", "百", "千", "万", "十", "百", "千", "亿", "十", "百", "千", "万亿"};
        // 将输入数字转换为字符串
        String result = String.valueOf(number);
        // 将该字符串分割为数组存放
        char[] ch = result.toCharArray();
        //结果 字符串
        StringBuilder str = new StringBuilder();
        int length = ch.length;
        for (int i = 0; i < length; i++) {
            int c = (int) ch[i] - 48;
            if (c != 0) {
                str.append(num[c - 1]).append(unit[length - i - 1]);
            }
        }
        return str;
    }

}
